#include <pd_gpio.h>
#include <pd_i2c_hd.h>
#include <hd_oled_protocol_i2c.h>
#include <hd_oled.h>
#include <delay.h>
#include <hd_flash.h>
#include "main.h"

#define OLED_PROTOCOL       2   // Select a protocol. SPI, 1; I2C, 2;
#define OLED_PROTOCOL_IMPL  2   // Select a implement. simulate, 1; hardware, 2;

static OLED *oled;
static uint8_t count = 0;

void oled_instance();

extern void SystemClock_Config(void);

int main(void) {
    SystemClock_Config();

    NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_2);

    oled_instance();
    oled->showString(oled, 0, 0, "Standby Mode");
    oled->refresh(oled);

    // Enable PWR clock:
    LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_PWR);
    /**
     * PWR_CSR bit 8: EWUP, Enable WKUP pin
     *      0: WKUP pin is used for general purpose I/O.
     *      1: WKUP pin is used for wakeup from Standby mode and forced in input pull down
     *         configuration (rising edge on WKUP pin wakes-up the system from Standby mode)
     */
    LL_PWR_EnableWakeUpPin(LL_PWR_WAKEUP_PIN1);


    while (1) {
        oled->showString(oled,0, 16, "Running");
        oled->refresh(oled);
        delay_ms(200);
        oled->showString(oled,0, 16, "       ");
        oled->refresh(oled);
        delay_ms(200);

        oled->showString(oled,0, 32, "Standby");
        oled->refresh(oled);
        delay_ms(1000);
        oled->showString(oled,0, 32, "       ");
        oled->refresh(oled);
        delay_ms(200);

        oled->clear(oled);
        oled->refresh(oled);

        // Set Standby Mode
        // SCB_SCR SLEEPDEEP
        LL_LPM_EnableDeepSleep();
        /**
         * PWR_CR bit1: PDDS
         *     0: Enter Stop mode when the CPU enters deepsleep. The regulator status depends on the LPDS bit.
         *     1: Enter Standby mode when the CPU enters deepsleep.
         */
        LL_PWR_SetPowerMode(LL_PWR_MODE_STANDBY);
        /**
         * PWR_CR bit2: CWUF, Clear wakeup flag
         *      This bit is always read as 0.
         *      0: No effect
         *      1: Clear the WUF Wakeup Flag after 2 System clock cycles
         */
        LL_PWR_ClearFlag_WU();
        __WFI();

        // After the MCU is woken up from Standby mode, it will start running from the beginning of the main()
        // function, thus there is no need to re-initialize the clock.
    }

}

void oled_instance() {
#if OLED_PROTOCOL == 1
    GPIO *sckGPIO = GPIOBuilder.build(GPIOA, LL_GPIO_PIN_5); // Clock, SPIx_SCK
    GPIO *sdaGPIO = GPIOBuilder.build(GPIOA, LL_GPIO_PIN_7); // MOSI,  SPIx_MOSI
    GPIO *resGPIO = GPIOBuilder.build(GPIOA, LL_GPIO_PIN_3); // Reset, General IO, the chip reset pin is better
    GPIO *dcGPIO = GPIOBuilder.build(GPIOA, LL_GPIO_PIN_1);  // Data or Command, General IO
    GPIO *csGPIO = GPIOBuilder.build(GPIOA, LL_GPIO_PIN_2);  // Chip Select(Slave Select), General IO
    SPIGPIO *spiGPIO = SPIGPIOBuilder.build(&csGPIO, 1, sckGPIO, sdaGPIO, NULL);

#if OLED_PROTOCOL_IMPL == 1
    SPISimulate *spiObj = SPISimulateBuilder.build(spiGPIO);
#elif OLED_PROTOCOL_IMPL == 2
    SPIHardware *spiObj = SPIHardwareBuilder.build(SPI1, spiGPIO);
#endif
    SPI *spi1 = (SPI *) spiObj;
    spi1->init(spi1);
    OLEDProtocolSPI *oledProtocolObj = OLEDProtocolSPIBuilder.build(spi1, resGPIO, dcGPIO, csGPIO);

#elif OLED_PROTOCOL == 2
    GPIO *sckGPIO = GPIOBuilder.build(GPIOB, LL_GPIO_PIN_10);  // I2Cx_SCL
    GPIO *sdaGPIO = GPIOBuilder.build(GPIOB, LL_GPIO_PIN_11);  // I2Cx_SDA
#if OLED_PROTOCOL_IMPL == 1
    I2CSimulate *i2CObj = I2CSimulateBuilder.build(sckGPIO, sdaGPIO);
#elif OLED_PROTOCOL_IMPL == 2
    I2CHardware *i2CObj = I2CHardwareBuilder.build(I2C2, sckGPIO, sdaGPIO);
#endif
    I2C *i2c = (I2C *) i2CObj;
    i2c->init(i2c);
    OLEDProtocolI2C *oledProtocolObj = OLEDProtocolI2CBuilder.build(i2c);
#endif

    StorageFlash *storageFlash = StorageFlashBuilder.build();
    OLEDFont *oledFont = OLEDFontBuilder.build((Storage *) storageFlash, ENCODING_UTF8);
    oled = OLEDBuilder.build((OLEDProtocol *) oledProtocolObj, oledFont);
    oled->init(oled);
}
